package tx.oopa.circuit.io;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import tx.oopa.circuit.node.AbstractEdgingNode;
import tx.oopa.circuit.node.AbstractNode;
import tx.oopa.circuit.node.EndingNode;
import tx.oopa.circuit.node.NodeObserver;
import tx.oopa.circuit.node.PortNode;
import tx.oopa.circuit.node.StartingNode;
import tx.oopa.circuit.port.AbstractComponent;
import tx.oopa.circuit.port.AbstractInput;
import tx.oopa.circuit.port.AbstractPort;
import tx.oopa.circuit.port.PortFactory;
import tx.oopa.circuit.port.impl.Probe;

public class CircuitFileStrategy implements IFileStrategy {

    private static final String COMMENT = "#";
    private static final String SPLITTER = ":";
    private static final String EDGE_SPLITTER = ",";
    private static final String LINE_FEED = ";";

    /**
     * Reads the specified file and returns its contents as a list of AbstractNodes
     * 
     * @param path
     *            The path to the file
     * @return The content of the file
     */
    public Object readFromFile(String path) throws IOException {
        if(path == null) {
            throw new IllegalArgumentException();
        }

        BufferedReader reader = new BufferedReader(new InputStreamReader(
                new FileInputStream(path), "US-ASCII"));

        List<AbstractNode> nodes = new ArrayList<AbstractNode>();
        String line = null;
        while((line = reader.readLine()) != null) {
            if(line.isEmpty() || line.startsWith(COMMENT)) {
                continue;
            }

            line = getStrippedLine(line);

            String sourceID = line.split(SPLITTER)[0];
            String targetID = line.split(SPLITTER)[1];

            //Create nodes
            if(indexOfID(nodes, sourceID) == -1) {
                AbstractComponent port = PortFactory.createComponent(targetID);
                if(port != null) {
                    AbstractNode node = null;
                    if(port instanceof AbstractInput) {
                        node = new StartingNode();
                    } else if(port instanceof AbstractPort) {
                        node = new PortNode();
                    } else if(port instanceof Probe) {
                        node = new EndingNode();
                    }
                    node.setName(sourceID);
                    node.setComponent(port);
                    nodes.add(node);
                }
            //Define edges
            } else {
                AbstractEdgingNode sourceNode = null;
                int index = indexOfID(nodes, sourceID);
                if(index >= 0) {
                    sourceNode = (AbstractEdgingNode)nodes.get(index);

                    for(String port : targetID.split(EDGE_SPLITTER)) {
                        index = indexOfID(nodes, port);
                        if(index >= 0) {
                            AbstractNode targetNode = nodes.get(index);
                            sourceNode.addEdge((NodeObserver)targetNode); 
                        }
                    }
                }
            }
        }

        return nodes;
    }

    /**
     * Checks whether the specified path exists
     * 
     * @param path
     *            The path to check
     * @return Whether the specified path exists
     */
    public boolean fileIsValid(String path) throws IOException {
        if(path == null) {
            throw new IllegalArgumentException();
        }

        File file = new File(path);
        if(!file.exists()) {
            return false;
        }

        BufferedReader reader = new BufferedReader(new InputStreamReader(
                new FileInputStream(path), "US-ASCII"));

        try {
            List<String> ports = new ArrayList<String>();
            String line = null;
            while((line = reader.readLine()) != null) {
                if(line.isEmpty() || line.startsWith(COMMENT)) {
                    continue;
                }
                if(!line.endsWith(LINE_FEED)) {
                    return false;
                }
                if(!line.contains(SPLITTER)) {
                    return false;
                }

                line = getStrippedLine(line);

                if(line.split(SPLITTER).length != 2) {
                    return false;
                }
                String sourceID = line.split(SPLITTER)[0];
                if(!sourceID.matches("[a-zA-Z0-9_]*")) {
                    return false;
                }

                String targetID = line.split(SPLITTER)[1];
                if(!ports.contains(sourceID)) {
                    if(PortFactory.createComponent(targetID) == null) {
                        return false;
                    }
                    ports.add(sourceID);
                } else {
                    if(sourceID.equals(targetID)) {
                        return false;
                    }
                    for(String port : targetID.split(EDGE_SPLITTER)) {
                        if(!ports.contains(port)) {
                            return false;
                        }
                    }
                }
            }
        } finally {
            reader.close();
        }

        return true;
    }

    private String getStrippedLine(String line) {
        //Strip line
        line = line.replaceAll("\\s", "");
        line = line.replaceAll(LINE_FEED, "");
        if(line.contains(COMMENT)) {
            line = line.split(COMMENT)[0];
        }
        return line;
    }
    
    private int indexOfID(List<AbstractNode> nodes, String ID) {
        for(int i = 0; i < nodes.size(); i++) {
            AbstractNode node = nodes.get(i);
            if(node.getName().equals(ID)) {
                return i;
            }
        }
        return -1;
    }
}
